/*
 * MIT License
 *
 * Copyright (c) 2021 Mr.css
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

package cn.seaboot.excel.call;

import cn.seaboot.commons.reflect.FieldAccess;
import cn.seaboot.commons.reflect.ObjectField;
import cn.seaboot.excel.ExcelCell;
import cn.seaboot.excel.ExcelException;
import cn.seaboot.excel.header.Header;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Workbook;
import org.jetbrains.annotations.NotNull;

import java.util.Arrays;

/**
 * 对象的读取，每一次读取，需要重新声明ObjectReaderAndWriter
 * Read row of sheet between startRow and lastRow, and fill data into a list.
 *
 * @author Mr.css
 * @version 2021-01-20 16:05
 */
public class ObjectReaderAndWriter<T> implements RowReaderAndWriter<T> {
    /**
     * 字段名，用于确定对象的取值顺序
     */
    private final String[] fieldNames;
    /**
     * 目标类型
     */
    private final Class<T> target;
    /**
     * 字段数组
     */
    private final ObjectField[] fields;

    /**
     * constructor
     *
     * @param fieldNames fieldNames 不指定，则使用注解确定顺序
     * @param target     targetType
     */
    public ObjectReaderAndWriter(@NotNull Class<T> target, @NotNull String[] fieldNames) {
        this.fieldNames = fieldNames;
        this.target = target;
        this.fields = this.getFieldByHeader(target);
    }

    /**
     * constructor
     *
     * @param headers fieldNames 不指定，则使用注解确定顺序
     * @param target     targetType
     */
    public ObjectReaderAndWriter(@NotNull Class<T> target, @NotNull Header[] headers) {
        this.fieldNames = Arrays.stream(headers).map(Header::getLabel).toArray(String[]::new);
        this.target = target;
        this.fields = this.getFieldByHeader(target);
    }

    /**
     * 通过字段名确定取值顺序。
     * get field array by fieldNames
     *
     * @param clazz Class
     * @return Field array
     * @throws ExcelException -
     */
    private ObjectField[] getFieldByHeader(Class<T> clazz) {
        FieldAccess fieldAccess = FieldAccess.create(clazz);
        String[] names = this.fieldNames;
        ObjectField[] res = new ObjectField[names.length];
        for (int i = 0; i < names.length; i++) {
            res[i] = fieldAccess.getField(names[i]);
        }
        return res;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public T readRow(@NotNull Row row) throws ExcelException {
        try {
            T inst = target.newInstance();
            for (int i = 0; i < fields.length; i++) {
                ObjectField field = fields[i];
                if (field != null) {
                    Object object = ExcelCell.readValue(row.getCell(i));
                    field.setValue(inst, object);
                }
            }
            return inst;
        } catch (InstantiationException | IllegalAccessException e) {
            throw new ExcelException("Can not create instance: " + target, e);
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void writeRow(Row row, T obj) {
        for (int i = 0; i < fields.length; i++) {
            ObjectField field = fields[i];
            if (field != null) {
                ExcelCell.addCell(row, i, fields[i].getValue(obj));
            }
        }
    }

    @Override
    public void startup(Workbook book) {

    }
}